import { Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, Types } from 'mongoose';
import { LcDocument, LcProject } from '../../types/editor.type';
import { ITypeEntity } from '../../../superType/types';
import { getDocumentPath, getTypePath } from '../virtual-file-system/utils/source-path';
import { generateDocumentCode } from '../code-transformer/schemaToCode/fileGen';
import { getTypeFileCode } from '../virtual-file-system/adapters/pipe/getFileCode';
import { FileSystemFile, InitializationResult } from './dto/vfs.dto';

/**
 * 文件系统初始化服务
 * 负责项目打开时的文件系统构建、哈希计算和缓存更新
 */
@Injectable()
export class FileSystemService {
  private readonly logger = new Logger(FileSystemService.name);

  constructor(
    @InjectModel('doc') private documentModel: Model<LcDocument>,
    @InjectModel('type') private typeModel: Model<ITypeEntity>,
  ) {}

  /**
   * 初始化项目文件系统
   * @param projectId 项目ID
   * @param project 项目对象（包含 documents）
   * @returns 初始化结果
   */
  async initializeProjectFileSystem(
    projectId: string,
    project: LcProject,
  ): Promise<InitializationResult> {
    const startTime = Date.now();
    const result: InitializationResult = {
      success: false,
      projectId,
      totalFiles: 0,
      generatedFiles: 0,
      updatedDocuments: 0,
      updatedTypes: 0,
      errors: [],
      duration: 0,
    };

    try {
      this.logger.log(`开始初始化项目文件系统: ${projectId}`);

      // 1. 生成所有文件
      const allFiles = await this.generateAllFiles(project);
      result.totalFiles = allFiles.length;
      result.generatedFiles = allFiles.filter((f) => f.type === 'file').length;

      this.logger.log(`生成了 ${allFiles.length} 个文件条目`);

      // 2. 批量更新文档的 CSS 和哈希信息
      const documentUpdateResult = await this.updateDocumentsHashes(
        projectId,
        allFiles.filter((f) => f.entityType === 'document'),
      );
      result.updatedDocuments = documentUpdateResult.updated;

      // 3. 批量更新类型实体的哈希信息
      const typeUpdateResult = await this.updateTypesHashes(
        projectId,
        allFiles.filter((f) => f.entityType === 'type'),
      );
      result.updatedTypes = typeUpdateResult.updated;

      // 4. 构建文件系统结构（用于缓存）
      const fileSystemStructure = this.buildFileSystemStructure(allFiles);

      // TODO: 5. 更新缓存（Redis）
      // await this.updateFileSystemCache(projectId, fileSystemStructure);

      result.success = true;
      result.duration = Date.now() - startTime;

      this.logger.log(`文件系统初始化完成`, {
        projectId,
        duration: `${result.duration}ms`,
        totalFiles: result.totalFiles,
        updatedDocuments: result.updatedDocuments,
        updatedTypes: result.updatedTypes,
      });
    } catch (error) {
      result.errors.push(error.message);
      result.duration = Date.now() - startTime;
      this.logger.error(`文件系统初始化失败`, error.stack);
    }

    return result;
  }

  /**
   * 生成所有文件条目
   */
  private async generateAllFiles(project: LcProject): Promise<FileSystemFile[]> {
    const files: FileSystemFile[] = [];

    // 1. 生成文档相关文件
    if (project.documents) {
      for (const doc of project.documents) {
        const docFiles = await this.generateDocumentFiles(doc);
        files.push(...docFiles);
      }
    }

    // 2. 生成类型实体文件
    const types = await this.typeModel.find({ projectId: new Types.ObjectId(project._id!) }).lean();

    for (const type of types) {
      const typeFiles = await this.generateTypeFiles(type);
      files.push(...typeFiles);
    }

    // 3. 生成项目配置文件
    const configFiles = this.generateProjectConfigFiles(project);
    files.push(...configFiles);

    return files;
  }

  /**
   * 生成文档相关文件（TSX + CSS）
   */
  private async generateDocumentFiles(doc: Partial<LcDocument>): Promise<FileSystemFile[]> {
    const files: FileSystemFile[] = [];
    const modifyTime = doc.update_time || Date.now();

    if (!doc.fileName) return files;

    // 应用路径映射（与 EditorAdapter 保持一致）
    const mappedDoc = this.applyDocumentPathMapping(doc);
    const docPath = getDocumentPath(mappedDoc as LcDocument);

    // 1. 生成 TSX 文件
    try {
      const tsxContent = await generateDocumentCode(doc as LcDocument, {
        generateRender: true,
      });
      const tsxHash = this.generateHash(tsxContent);

      files.push({
        name: `${doc.fileName}.tsx`,
        path: docPath.replace('.doc', '.tsx'),
        type: 'file',
        content: tsxContent,
        hash: tsxHash,
        modifyTime,
        entityType: 'document',
      });
    } catch (error) {
      this.logger.error(`生成 TSX 失败 ${doc.fileName}`, error.stack);
    }

    // 2. 生成 CSS 文件（如果有）
    let cssContent = '';
    let cssHash = '';

    if (doc.schema) {
      try {
        const schema = typeof doc.schema === 'string' ? JSON.parse(doc.schema) : doc.schema;
        cssContent = schema.css || '';
        cssHash = cssContent ? this.generateHash(cssContent) : '';

        if (cssContent) {
          files.push({
            name: `${doc.fileName}.css`,
            path: docPath.replace('.doc', '.css'),
            type: 'file',
            content: cssContent,
            hash: cssHash,
            modifyTime,
            entityType: 'document',
          });
        }
      } catch (error) {
        this.logger.error(`解析 schema.css 失败 ${doc.fileName}`, error.stack);
      }
    }

    return files;
  }

  /**
   * 生成类型实体文件
   */
  private async generateTypeFiles(type: ITypeEntity): Promise<FileSystemFile[]> {
    const files: FileSystemFile[] = [];

    if (type.type === 'folder') {
      // 文件夹类型
      if (type.folderPath) {
        files.push({
          name: type.folderPath.split('/').pop() || '',
          path: type.folderPath,
          type: 'directory',
          modifyTime: type.update_time,
          entityType: 'type',
        });
      }
    } else {
      // 其他类型文件
      try {
        const content = await getTypeFileCode(type);
        const hash = this.generateHash(content);
        const typePath = getTypePath(type);

        files.push({
          name: type.name + this.getTypeFileExtension(type),
          path: typePath,
          type: 'file',
          content,
          hash,
          modifyTime: type.update_time,
          entityType: 'type',
        });
      } catch (error) {
        this.logger.error(`生成类型文件失败 ${type.name}`, error.stack);
      }
    }

    return files;
  }

  /**
   * 生成项目配置文件
   */
  private generateProjectConfigFiles(project: LcProject): FileSystemFile[] {
    const files: FileSystemFile[] = [];
    const modifyTime = project.update_time || Date.now();

    // project.json
    const projectConfig = {
      id: project._id,
      title: project.title,
      description: project.description,
      type: project.type,
      teamId: project.teamId,
      appConfig: project.appConfig,
      envInfo: project.envInfo,
      create_time: project.create_time,
      update_time: project.update_time,
    };

    files.push({
      name: 'project.json',
      path: '/project.json',
      type: 'file',
      content: JSON.stringify(projectConfig, null, 2),
      hash: this.generateHash(JSON.stringify(projectConfig)),
      modifyTime,
      entityType: 'document',
    });

    // assets.json
    if (project.packages || project.assets) {
      const assetsConfig = {
        packages: project.packages,
        assets: project.assets,
      };

      files.push({
        name: 'assets.json',
        path: '/assets.json',
        type: 'file',
        content: JSON.stringify(assetsConfig, null, 2),
        hash: this.generateHash(JSON.stringify(assetsConfig)),
        modifyTime,
        entityType: 'document',
      });
    }

    return files;
  }

  /**
   * 批量更新文档的 CSS 和哈希信息
   */
  private async updateDocumentsHashes(
    projectId: string,
    documentFiles: FileSystemFile[],
  ): Promise<{ updated: number; errors: string[] }> {
    const errors: string[] = [];
    let updated = 0;

    // 按 CSS 文件分组
    const cssFiles = documentFiles.filter((f) => f.path.endsWith('.css'));

    for (const cssFile of cssFiles) {
      try {
        const fileName = cssFile.name.replace('.css', '');

        await this.documentModel.updateOne(
          {
            projectId: new Types.ObjectId(projectId),
            fileName: fileName,
          },
          {
            $set: {
              css: cssFile.content || '',
              cssHash: cssFile.hash || '',
              cssUpdateAt: Date.now(),
            },
          },
        );

        updated++;
      } catch (error) {
        errors.push(`更新文档 ${cssFile.name} 失败: ${error.message}`);
      }
    }

    return { updated, errors };
  }

  /**
   * 批量更新类型实体的哈希信息
   */
  private async updateTypesHashes(
    projectId: string,
    typeFiles: FileSystemFile[],
  ): Promise<{ updated: number; errors: string[] }> {
    const errors: string[] = [];
    let updated = 0;

    for (const typeFile of typeFiles) {
      try {
        const fileName = typeFile.name.replace(/\.[^/.]+$/, ''); // 移除扩展名

        await this.typeModel.updateOne(
          {
            projectId: new Types.ObjectId(projectId),
            name: fileName,
          },
          {
            $set: {
              hash: typeFile.hash || '',
              update_time: Date.now(),
            },
          },
        );

        updated++;
      } catch (error) {
        errors.push(`更新类型 ${typeFile.name} 失败: ${error.message}`);
      }
    }

    return { updated, errors };
  }

  /**
   * 构建文件系统结构（用于缓存）
   */
  private buildFileSystemStructure(files: FileSystemFile[]) {
    const structure = {
      files: new Map<string, FileSystemFile>(),
      directories: new Map<string, Set<string>>(),
    };

    for (const file of files) {
      structure.files.set(file.path, file);

      // 构建目录结构
      const parts = file.path.split('/').filter(Boolean);
      let currentPath = '';

      for (let i = 0; i < parts.length - 1; i++) {
        currentPath += '/' + parts[i];
        if (!structure.directories.has(currentPath)) {
          structure.directories.set(currentPath, new Set());
        }
      }

      // 添加文件到目录
      const parentDir = currentPath || '/';
      if (!structure.directories.has(parentDir)) {
        structure.directories.set(parentDir, new Set());
      }
      structure.directories.get(parentDir)!.add(file.path);
    }

    return structure;
  }

  /**
   * 应用文档路径映射（与 EditorAdapter 保持一致）
   */
  private applyDocumentPathMapping(doc: Partial<LcDocument>): Partial<LcDocument> {
    const mappedDoc = { ...doc };

    if (mappedDoc.folder?.startsWith('/pages')) {
      mappedDoc.folder = mappedDoc.folder.replace('/pages', '/src/pages');
    }
    if (mappedDoc.folder?.startsWith('/components')) {
      mappedDoc.folder = mappedDoc.folder.replace('/components', '/src/components');
    }
    if (mappedDoc.folder?.startsWith('/modules')) {
      mappedDoc.folder = mappedDoc.folder.replace('/modules', '/src/modules');
    }

    return mappedDoc;
  }

  /**
   * 获取类型文件的扩展名
   */
  private getTypeFileExtension(type: ITypeEntity): string {
    switch (type.type) {
      case 'action':
        return '.utils.ts';
      case 'api':
        return '.api.ts';
      case 'data':
        return '.json';
      case 'code':
        return '.ts';
      default:
        return '.ts';
    }
  }

  /**
   * 生成哈希值（跨平台兼容）
   */
  private generateHash(content: string): string {
    if (!content) return '';

    // 标准化内容（处理不同操作系统的差异）
    const normalizedContent = content.replace(/\r\n/g, '\n').trim();

    // 使用 SHA256 算法
    const crypto = require('crypto');
    return crypto.createHash('sha256').update(normalizedContent, 'utf8').digest('hex');
  }

  /**
   * 获取项目所有文件（轻量级，用于显示）
   */
  async getAllFiles(
    projectId: string,
  ): Promise<
    Array<{ name: string; path: string; type: 'file' | 'directory'; modifyTime?: number }>
  > {
    try {
      // 1. 获取文档信息（只查询必要字段）
      const documents = await this.documentModel
        .find({ projectId: new Types.ObjectId(projectId) })
        .select('fileName folder type update_time css cssHash')
        .lean();

      const files: Array<{
        name: string;
        path: string;
        type: 'file' | 'directory';
        modifyTime?: number;
      }> = [];

      // 2. 处理文档文件
      for (const doc of documents) {
        if (!doc.fileName) continue;

        const mappedDoc = this.applyDocumentPathMapping(doc);
        const docPath = getDocumentPath(mappedDoc);
        const modifyTime = doc.update_time || Date.now();

        // 添加 TSX 文件
        files.push({
          name: `${doc.fileName}.tsx`,
          path: docPath.replace('.doc', '.tsx'),
          type: 'file',
          modifyTime,
        });

        // 如果有 CSS，添加 CSS 文件
        if (doc.css && doc.css.trim() !== '') {
          files.push({
            name: `${doc.fileName}.css`,
            path: docPath.replace('.doc', '.css'),
            type: 'file',
            modifyTime,
          });
        }
      }

      // 3. 处理类型文件
      const types = await this.typeModel
        .find({ projectId: new Types.ObjectId(projectId) })
        .select('name type folderPath update_time');

      for (const type of types) {
        const modifyTime = type.update_time || Date.now();

        if (type.type === 'folder') {
          if (type.folderPath) {
            files.push({
              name: type.folderPath.split('/').pop() || '',
              path: type.folderPath,
              type: 'directory',
              modifyTime,
            });
          }
        } else {
          const typePath = getTypePath(type);
          files.push({
            name: type.name + this.getTypeFileExtension(type),
            path: typePath,
            type: 'file',
            modifyTime,
          });
        }
      }

      return files.sort((a, b) => a.name.localeCompare(b.name));
    } catch (error) {
      this.logger.error('获取所有文件失败', error.stack);
      throw new Error(`获取项目文件失败: ${error.message}`);
    }
  }
}
